#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <hal_boot2.h>

#include "hilink_ota_adaptor.h"
#include "bl_mtd.h"

#define OPE_OK               (0)
#define OPE_NG               (-1)
#define MTD_DEVICE_OPEN_OK   (0)
#define OTA_MAX_FLASH_SIZE   (0xA8000)
#define OTA_HEADER_SIZE      (512)

static bl_mtd_handle_t handle;
static unsigned int flash_offset = 0;
unsigned char buf_tmp[513];

bool HalHilinkOtaAdapterFlashInit(void) {
    if ((MTD_DEVICE_OPEN_OK != bl_mtd_open(BL_MTD_PARTITION_NAME_FW_DEFAULT, &handle, BL_MTD_OPEN_FLAG_BACKUP))) {
        printf("error when get FW[1] ota partition\r\n");
        bl_mtd_close(handle);
        return;
    }
    return true;
}

unsigned int HalHilinkOtaAdapterGetUpdateIndex(void) {
    HALPartition_Entry_Config ptEntry;
    return BOOT2_PARTITION_TYPE_FW;
}

int HalHilinkOtaAdapterFlashErase(unsigned int size) {
    (void)size;
    bl_mtd_erase_all(handle);
    printf("FUNC:%s Called!!\r\n", __FUNCTION__);
    return OPE_OK;   
}

int HalHilinkOtaAdapterFlashWrite(const unsigned char *buf, unsigned int bufLen)
{
    if ((buf == NULL)) {
        printf("[CHKERR]FUNC:%s buf == NULL\r\n", __FUNCTION__);
        bl_mtd_erase_all(handle);
        bl_mtd_close(handle);
        return OPE_NG;
    }

    if (OTA_HEADER_SIZE > flash_offset) {
        memmove(&buf_tmp[flash_offset], buf, (OTA_HEADER_SIZE * sizeof(unsigned char)));
        printf("[WROK]FUNC:%s first write %d bytes is ota header!!!!\r\n", __FUNCTION__, (int)(OTA_HEADER_SIZE - flash_offset));
        flash_offset = OTA_HEADER_SIZE;
        return OPE_OK;
    }

    if (0 != bufLen) {
        if ((flash_offset - OTA_HEADER_SIZE + bufLen) > OTA_MAX_FLASH_SIZE) {
            printf("ota size exceed!\r\n");
            bufLen = OTA_MAX_FLASH_SIZE - flash_offset;
        }
        bl_mtd_write(handle, (flash_offset - OTA_HEADER_SIZE), bufLen, buf);
        flash_offset += bufLen;
        printf("[WROK]FUNC:%s write bufLen:%d to flash; flash_offset:%d\r\n", __FUNCTION__, (int)(bufLen), (int)(flash_offset));
    } else {
        printf("[ERR]FUNC:%s write bufLen:%d to flash; flash_offset:%d\r\n", __FUNCTION__, (int)(bufLen), (int)(flash_offset));
    }

    return OPE_OK;
}

int HalHilinkOtaAdapterFlashRead(unsigned int offset, unsigned char *buf, unsigned int bufLen)
{
    if (NULL == buf) {
        printf("[CHKERR]FUNC:%s buf == NULL\r\n", __FUNCTION__);
        return OPE_NG;
    }

    if (0 == offset) {
        memmove(buf, &buf_tmp[0], (OTA_HEADER_SIZE * sizeof(unsigned char)));
        printf("FUNC:%s First Read Temp OTA HEADER\r\n", __FUNCTION__);
        return OPE_OK;
    }

    if (0 != bufLen) {
        bl_mtd_read(handle, (offset - OTA_HEADER_SIZE), bufLen, buf);
        printf("[RDOK]FUNC:%s read_offset:%d---bufLen:%d\r\n", __FUNCTION__, (int)(offset), (int)(bufLen));
    } else {
        printf("[RDERR]FUNC:%s read_offset:%d---bufLen:%d\r\n", __FUNCTION__, (int)(offset), (int)(bufLen));
    }

    return OPE_OK;
}

bool HalHilinkOtaAdapterFlashFinish(void) {
    int ret = 0;
    HALPartition_Entry_Config ptEntry;

    bl_mtd_close(handle);
    ret = hal_boot2_get_active_entries(BOOT2_PARTITION_TYPE_FW, &ptEntry);
    if (0 != ret) {
        printf("[CHKERR]FUNC:%s hal_boot2_get_active_entries ret Err!\r\n", __FUNCTION__);
        return OPE_NG;
    } else {
        printf("hal_boot2_get_active_entries ret OK\r\n");
    }

    printf("HalHilinkOtaAdapterFlashFinish:flash_offset:%d\r\n", (int)(flash_offset));
    ptEntry.len = flash_offset;
    ret = hal_boot2_update_ptable(&ptEntry);
    if (0 != ret) {
        printf("hal_boot2_update_ptable ret = %d\r\n", ret);
        return OPE_NG;
    } else {
        printf("hal_boot2_update_ptable ret OK\r\n");
    }
      
    flash_offset = 0;
    return true;
}

unsigned int HalHilinkOtaAdapterFlashMaxSize(void) {
    printf("HalHilinkOtaAdapterFlashMaxSize Called! return flashSize:%d\r\n", OTA_MAX_FLASH_SIZE);
    flash_offset = 0;
    return OTA_MAX_FLASH_SIZE;
}

void HalHilinkOtaAdapterRestart(int flag) {
    (void)flag;
    printf("HalHilinkOtaAdapterRestart Called!!!!----hal_reboot\r\n");
    hal_reboot();
}
